# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
from typing import Dict, Union, List
from exception import AppException
from kernels.utils import RequestUtil
from common.enums.notice import NoticeEnum
from common.enums.models import ModelsEnum
from common.enums.voice import VoiceEnum
from common.utils.config import ConfigUtil
from common.utils.tools import ToolsUtil
from common.utils.urls import UrlUtil
from common.models.dev import DevDiyModel
from common.models.dev import DevModelsModel
from common.models.notice import NoticeRecord
from plugins.msg.driver import MsgDriver
from apps.api.schemas import index_schema as schema


class IndexService:
    """ 公共服务类 """

    @classmethod
    async def config(cls) -> schema.ConfigVo:
        """
        全局配置

        Returns:
            schema.ConfigVo: 全局配置Vo

        Author:
            zero
        """
        pc = await ConfigUtil.get("pc") or {}
        login = await ConfigUtil.get("login") or {}
        dialog = await ConfigUtil.get("dialog") or {}
        website = await ConfigUtil.get("website") or {}
        bulletin = await ConfigUtil.get("bulletin") or {}

        rechargeSwitch = bool(int(await ConfigUtil.get(type_="recharge", key="status") or "0"))
        broadcastSwitch = bool(int(await ConfigUtil.get(type_="voice_speech", key="is_open") or "0"))

        v = bytes.fromhex("202d20506f7765726564206279206d646461692e636e21").decode("utf-8")
        s = pc.get("title", "") + v if RequestUtil.licEmpower == "study" else ""
        t = True if RequestUtil.licEmpower == "study" else False
        return schema.ConfigVo(
            oss_domain=await UrlUtil.get_storage_domain(),
            login={
                "is_agreement": int(login.get("is_agreement", 0)),
                "defaults": login.get("defaults", ""),
                "register": login.get("registers", []),
                "means": login.get("login_modes", []),
                "oauth": login.get("login_other", []),
            },
            website={
                "icp": website.get("icp", ""),
                "pcp": website.get("pcp", ""),
                "domain": website.get("domain", ""),
                "analyse": website.get("analyse", ""),
                "copyright": website.get("copyright", ""),
                "customer_link": website.get("customer_link", "")
            },
            pc={
                "favicon": await UrlUtil.to_absolute_url(pc.get("favicon", "")),
                "logo": await UrlUtil.to_absolute_url(pc.get("logo", "")),
                "name": pc.get("name", ""),
                "title": s,
                "keywords": pc.get("keywords", ""),
                "description": pc.get("description", "")
            },
            dialog={
                "default_robot": dialog.get("default_robot", ""),
                "currency_unit": dialog.get("currency_unit", "") or "积分",
                "win_watermark": dialog.get("win_watermark", "")
            },
            switch={
                "restrict": t,
                "recharge": rechargeSwitch,
                "broadcast": broadcastSwitch
            },
            bulletin={
                "is_open": bool(bulletin.get("is_open", 0)),
                "title": bulletin.get("title", ""),
                "content": bulletin.get("content", "")
            }
        )

    @classmethod
    async def models(cls) -> Dict[str, Union[List[schema.AiModelsVo], List[schema.AiBroadcast]]]:
        """
        获取模型

        Returns:
            Dict[str, Union[List[schema.AiModelsVo], List[schema.AiBroadcast]]]

        Author:
            zero
        """
        chat_models = []
        vector_models = []
        ranking_models = []
        broadcast_models = []

        # 语言模型
        for scene in [ModelsEnum.TYPE_CHAT, ModelsEnum.TYPE_VECTOR, ModelsEnum.TYPE_RANKING]:
            lists = await (DevModelsModel
                           .filter(scene=scene)
                           .filter(is_disable=0, is_delete=0)
                           .order_by("sort", "-id")
                           .all())

            for item in lists:
                config = []
                if scene == ModelsEnum.TYPE_CHAT:
                    config = ModelsEnum.chat_models()[item.channel]["configs"][0]["config"]

                _itemVo = schema.AiModelsVo(
                    mark=item.mark,
                    name=item.name,
                    logo=await UrlUtil.to_absolute_url(item.logo),
                    config=config,
                    models=[]
                )

                _models = json.loads(item.models)
                for llm in _models:
                    if llm["status"]:
                        _itemVo.models.append(schema.AiModelsVo.Llm(
                            model=llm["model"],
                            alias=llm["alias"],
                            price=int(llm.get("price", 0)),
                            modal=bool(llm.get("modal", 0)),
                            max_output=int(llm.get("max_output", 0)),
                            max_context=int(llm.get("max_context", 0))
                        ))

                if len(_itemVo.models) <= 0:
                    continue

                if scene == ModelsEnum.TYPE_CHAT:
                    chat_models.append(_itemVo)
                elif scene == ModelsEnum.TYPE_VECTOR:
                    vector_models.append(_itemVo)
                elif scene == ModelsEnum.TYPE_RANKING:
                    ranking_models.append(_itemVo)

        # 语音模型
        cacheSpeech = await ConfigUtil.get("voice_speech", "configs") or {}
        for key, item in VoiceEnum.get_default_speech().items():
            config = cacheSpeech.get(key, {})
            merged = {**item, **config}
            if config.get("open"):
                broadcast_models.append(schema.AiBroadcast(
                    key=key,
                    name=merged.get("name"),
                    speed=float(merged.get("speed")),
                    speed_range=merged.get("speed_range"),
                    pronounces=merged.get("pronounces"),
                ))

        return {
            "chat_models": chat_models,
            "vector_models": vector_models,
            "ranking_models": ranking_models,
            "broadcast_models": broadcast_models
        }

    @classmethod
    async def diy(cls, scene: int):
        """
        装修数据

        Returns:
          dict: 装修数据Vo

        Author:
          zero
        """
        detail = await DevDiyModel.filter(scene=scene).first()
        return json.loads(detail.data)

    @classmethod
    async def monitor(cls, user_id: int) -> schema.MonitorVo:
        """
        消息监听

        Args:
            user_id: int

        Returns:
            schema.MonitorVo: 轮询监听数据Vo

        Author:
            zero
        """
        unread = await (NoticeRecord
                        .filter(user_id=user_id)
                        .filter(receiver=1)
                        .filter(is_read=0)
                        .filter(is_delete=0)
                        .filter(sender=NoticeEnum.SENDER_SYS)
                        .count())
        return schema.MonitorVo(
            unread=unread
        )

    @classmethod
    async def policy(cls, type_: str) -> schema.PolicyVo:
        """
        政策协议

        Returns:
            schema.PolicyVo: 政策协议Vo

        Author:
            zero
        """
        content = await ConfigUtil.get("policy", type_) or ""
        return schema.PolicyVo(
            content=content
        )

    @classmethod
    async def send_sms(cls, scene: int, mobile: str):
        """
        发送短信

        Args:
            scene (int): 发送场景码
            mobile (str): 接收手机号

        Author:
            zero
        """
        try:
            await MsgDriver.send(scene, {
                "mobile": mobile,
                "code": ToolsUtil.make_rand_digit(6)
            })
        except Exception as e:
            raise AppException(str(e))

    @classmethod
    async def send_email(cls, scene: int, email: str):
        """
        发送邮件

        Args:
            scene (int): 发送场景码
            email (str): 接收邮箱号

        Author:
            zero
        """
        try:
            await MsgDriver.send(scene, {
                "email": email,
                "code": ToolsUtil.make_rand_digit(6)
            })
        except Exception as e:
            raise AppException(str(e))
